﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.ViewModels;
using MugenMvvmToolkitTest.TestInfrastructure;
using MugenMvvmToolkitTest.TestModels;
using Should;

namespace MugenMvvmToolkitTest.ViewModel
{
    [TestClass]
    public class GridViewModelTest : TestBase
    {
        #region Nested types

        public class TestInternalEventGridViewModel : GridViewModel<TestGridModel>
        {
            #region Properties

            public TestGridModel SelectedItemChanging { get; set; }

            public TestGridModel SelectedItemChangingResult { get; set; }

            public TestGridModel SelectedItemChangedOld { get; set; }

            public TestGridModel SelectedItemChangedNew { get; set; }

            public IEnumerable<TestGridModel> ItemsSourceChangingValue { get; set; }

            public IEnumerable<TestGridModel> ItemsSourceChangingResult { get; set; }

            public IEnumerable<TestGridModel> ItemsSourceChangedValue { get; set; }

            #endregion

            #region Overrides of GridViewModelBase<TestGridModel>

            /// <summary>
            ///     Occurs when the <c>SelectedItem</c> property changing.
            /// </summary>
            /// <param name="newValue">The new value.</param>
            /// <returns>The value to set as selected item.</returns>
            protected override TestGridModel OnSelectedItemChanging(TestGridModel newValue)
            {
                SelectedItemChanging = newValue;
                return SelectedItemChangingResult;
            }

            /// <summary>
            ///     Occurs when the <c>SelectedItem</c> property changed.
            /// </summary>
            /// <param name="oldValue">The old value.</param>
            /// <param name="newValue">The new value.</param>
            protected override void OnSelectedItemChanged(TestGridModel oldValue, TestGridModel newValue)
            {
                SelectedItemChangedOld = oldValue;
                SelectedItemChangedNew = newValue;
            }

            /// <summary>
            ///     Occurs when the <c>ItemsSource</c> property changing.
            /// </summary>
            /// <param name="data">The new item source data.</param>
            /// <returns>
            ///     An instance of <see cref="IEnumerable{T}" />.
            /// </returns>
            protected override IEnumerable<TestGridModel> OnItemsSourceChanging(IEnumerable<TestGridModel> data)
            {
                ItemsSourceChangingValue = data;
                return ItemsSourceChangingResult;
            }

            /// <summary>
            ///     Occurs when the <c>ItemsSource</c> property changed.
            /// </summary>
            /// <param name="data">The new item source data.</param>
            protected override void OnItemsSourceChanged(IEnumerable<TestGridModel> data)
            {
                ItemsSourceChangedValue = data;
            }

            #endregion
        }

        #endregion

        #region Test methods

        [TestMethod]
        public void ItemsSourceShouldBeNullWhenVmCreated()
        {
            var gridViewModel = new GridViewModel<object>();
            gridViewModel.ItemsSource.ShouldBeNull();
        }

        [TestMethod]
        public void ItemsSourceChangedGenericShouldInvokeOnItemsSourceChanged()
        {
            bool isItemsSourceChanged = false;
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };

            var gridViewModel = GetViewModel<GridViewModel<TestGridModel>>();
            gridViewModel.ItemsSourceChanged += data =>
                                                {
                                                    if (isItemsSourceChanged)
                                                        throw new InvalidOperationException();
                                                    isItemsSourceChanged = true;
                                                    gridViewModel.ItemsSource.ShouldEqual(data);
                                                };
            gridViewModel.UpdateItemsSource(listItems);
            isItemsSourceChanged.ShouldBeTrue();
        }

        [TestMethod]
        public void ItemsSourceChangedNonGenericShouldInvokeOnItemsSourceChanged()
        {
            bool isItemsSourceChanged = false;
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };

            IGridViewModel gridViewModel = GetViewModel<GridViewModel<TestGridModel>>();
            gridViewModel.ItemsSourceChanged += data =>
            {
                if (isItemsSourceChanged)
                    throw new InvalidOperationException();
                isItemsSourceChanged = true;
                gridViewModel.ItemsSource.ShouldEqual(data);
            };
            gridViewModel.UpdateItemsSource(listItems);
            isItemsSourceChanged.ShouldBeTrue();
        }

        [TestMethod]
        public void SelectedItemChangedGenericShouldInvokeOnSelectedItemChanged()
        {
            bool isFocusedRowChanged = false;
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };

            var gridViewModel = GetViewModel<GridViewModel<TestGridModel>>();
            gridViewModel.SelectedItemChanged += (value, newValue) =>
                                                 {
                                                     if (isFocusedRowChanged)
                                                         throw new InvalidOperationException();
                                                     isFocusedRowChanged = true;
                                                     value.ShouldBeNull();
                                                     newValue.ShouldEqual(model);
                                                 };
            gridViewModel.UpdateItemsSource(listItems);
            gridViewModel.SelectedItem = model;
            isFocusedRowChanged.ShouldBeTrue();
        }

        [TestMethod]
        public void SelectedItemChangedNonGenericShouldInvokeOnSelectedItemChanged()
        {
            bool isFocusedRowChanged = false;
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };

            IGridViewModel gridViewModel = GetViewModel<GridViewModel<TestGridModel>>();
            gridViewModel.SelectedItemChanged += (value, newValue) =>
            {
                if (isFocusedRowChanged)
                    throw new InvalidOperationException();
                isFocusedRowChanged = true;
                value.ShouldBeNull();
                newValue.ShouldEqual(model);
            };
            gridViewModel.UpdateItemsSource(listItems);
            gridViewModel.SelectedItem = model;
            isFocusedRowChanged.ShouldBeTrue();
        }

        [TestMethod]
        public void WhenItemsSourceChangedSelectedItemShouldBeSetToNullTest()
        {
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };
            var gridViewModel = GetViewModel<GridViewModel<TestGridModel>>();

            gridViewModel.UpdateItemsSource(listItems);
            gridViewModel.SelectedItem = model;

            gridViewModel.SelectedItem.ShouldEqual(model);
            gridViewModel.UpdateItemsSource(null);
            gridViewModel.SelectedItem.ShouldBeNull();
        }

        [TestMethod]
        public void FilterShouldFilterRecords()
        {
            var model = new TestGridModel { Name = "test" };
            var model1 = new TestGridModel { Name = "test1" };
            var listItems = new List<TestGridModel> { model, model1 };

            var gridViewModel = GetViewModel<GridViewModel<TestGridModel>>();

            gridViewModel.Filter = item => item.Name == string.Empty;
            gridViewModel.UpdateItemsSource(listItems);
            gridViewModel.ItemsSource.Contains(model).ShouldBeFalse();
            gridViewModel.ItemsSource.Contains(model1).ShouldBeFalse();

            gridViewModel.Filter = item => true;
            gridViewModel.ItemsSource.Contains(model).ShouldBeTrue();
            gridViewModel.ItemsSource.Contains(model1).ShouldBeTrue();

            gridViewModel.Filter = item => item.Name == "test";
            gridViewModel.ItemsSource.Contains(model).ShouldBeTrue();
            gridViewModel.ItemsSource.Contains(model1).ShouldBeFalse();
        }

        [TestMethod]
        public void InternalSelectedRowEventTest()
        {
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };
            var viewModel = GetViewModel<TestInternalEventGridViewModel>();
            viewModel.ItemsSourceChangingResult = listItems;

            viewModel.UpdateItemsSource(listItems);
            viewModel.SelectedItem = model;
            viewModel.SelectedItemChanging.ShouldEqual(model);
            viewModel.SelectedItem.ShouldBeNull();

            viewModel.SelectedItemChangingResult = model;
            viewModel.SelectedItem = model;
            viewModel.SelectedItem.ShouldEqual(model);

            viewModel.SelectedItemChangedOld.ShouldBeNull();
            viewModel.SelectedItemChangedNew.ShouldEqual(model);
        }

        [TestMethod]
        public void InternalItemsSourceEventsTest()
        {
            var model = new TestGridModel { Name = "test" };
            var listItems = new List<TestGridModel> { model };
            var viewModel = GetViewModel<TestInternalEventGridViewModel>();

            viewModel.UpdateItemsSource(listItems);
            viewModel.ItemsSourceChangingValue.ShouldEqual(listItems);
            viewModel.ItemsSource.ShouldBeNull();

            viewModel.ItemsSourceChangingResult = listItems;
            viewModel.UpdateItemsSource(listItems);
            viewModel.ItemsSourceChangingValue.ShouldEqual(listItems);
            viewModel.ItemsSourceChangedValue.ShouldEqual(viewModel.ItemsSource);
            viewModel.ItemsSource.ShouldNotBeNull();
            viewModel.ItemsSource.All(listItems.Contains).ShouldBeTrue();
        }

        #endregion
    }
}